package com.zhou.system.management.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.zhou.common.lib.exception.BizException;
import com.zhou.common.lib.model.CommonPage;
import com.zhou.common.lib.util.BeanUtil;
import com.zhou.common.lib.util.PasswordUtil;
import com.zhou.system.management.constant.UserContext;
import com.zhou.system.management.entity.SysDeptUser;
import com.zhou.system.management.entity.SysUser;
import com.zhou.system.management.entity.SysUserChangePwdAo;
import com.zhou.system.management.mapper.SysUserMapper;
import com.zhou.system.management.model.ao.RestPwdAo;
import com.zhou.system.management.model.ao.SysUserAddAo;
import com.zhou.system.management.model.ao.SysUserEnableAo;
import com.zhou.system.management.model.ao.SysUserQueryAo;
import com.zhou.system.management.model.ao.SysUserUpdateAo;
import com.zhou.system.management.model.inner.DeptRoleInner;
import com.zhou.system.management.model.vo.DeptRoleVo;
import com.zhou.system.management.security.model.MyUserDetails;
import com.zhou.system.management.service.ISysUserService;
import com.zhou.system.management.util.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.zhou.system.management.enums.ManageErrorCodeEnum.USERNAME_IS_REPEAT_ERROR;
import static com.zhou.system.management.enums.ManageErrorCodeEnum.USER_HAS_NOT_DEPTS;
import static com.zhou.system.management.enums.ManageErrorCodeEnum.USER_NOT_EXISTS_ERROR;
import static com.zhou.system.management.enums.ManageErrorCodeEnum.USER_PASSWORD_INCORRECT_ERROR;
import static com.zhou.system.management.enums.ManageErrorCodeEnum.USER_PASSWORD_NEW_AND_OLD_SAME_ERROR;
import static com.zhou.system.management.enums.ManageErrorCodeEnum.USER_PASSWORD_NOT_SAME_ERROR;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author zxf
 * @since 2023-05-13
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Autowired
    private SysDeptUserServiceImpl deptUserService;

    @Override
    public SysUser loadUserByUsername(String username) {
        LambdaQueryWrapper<SysUser> queryWrapper = Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUserName, username);
        return getOne(queryWrapper);
    }

    @Override
    public void queryUserInfo() {
        //todo 查询当前用户的菜单

    }

    @Override
    public CommonPage<SysUser> queryUsers(SysUserQueryAo queryAo) {
        MyUserDetails myUserDetails = UserContext.currentUser();
        Long userId = myUserDetails.getUserId();
        IPage<SysUser> iPage = queryAo.toPage();
        if (UserUtil.isAdmin(userId)) {
            baseMapper.queryUsers(iPage, null, queryAo);
        } else {
            baseMapper.queryUsers(iPage, myUserDetails.getCurrOrgId(), queryAo);
        }
        return CommonPage.fromMybatisPage(iPage);
    }

    @Override
    public void restPwd(RestPwdAo ao) {
        String password = passwordEncoder.encode(ao.getPassword());
        SysUser user = new SysUser();
        user.setUserId(ao.getUserId());
        user.setPassword(password);
        user.setNeedModifyPwd(true);
        updateById(user);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String addUser(SysUserAddAo ao) {
        List<SysUserAddAo.DeptRoles> depts = ao.getDepts();
        List<SysUserAddAo.DeptRoles> validDepts =
                depts.stream().filter(t -> t.getStatus() == 0).collect(Collectors.toList());
        if (validDepts.size() == 0) {
            throw new BizException(USER_HAS_NOT_DEPTS);
        }
        if (baseMapper.selectCountByUsername(ao.getUserName()) > 0) {
            throw new BizException(USERNAME_IS_REPEAT_ERROR);
        }
        SysUser user = BeanUtil.copyObject(ao, SysUser.class);
        String password = PasswordUtil.generatePassword(10);
        user.setPassword(passwordEncoder.encode(password));
        user.setNeedModifyPwd(true);
        save(user);

        List<SysDeptUser> deptUsers = Lists.newArrayList();
        depts.forEach(dept -> {
            if (CollectionUtil.isEmpty(dept.getRoleIds())) {
                SysDeptUser sysDeptUser = new SysDeptUser();
                sysDeptUser.setUserId(user.getUserId());
                sysDeptUser.setDeptId(dept.getDeptId());
                sysDeptUser.setStatus(dept.getStatus());
                deptUsers.add(sysDeptUser);
            } else {
                dept.getRoleIds().forEach(role -> {
                    SysDeptUser sysDeptUser = new SysDeptUser();
                    sysDeptUser.setUserId(user.getUserId());
                    sysDeptUser.setDeptId(dept.getDeptId());
                    sysDeptUser.setRoleId(role);
                    sysDeptUser.setStatus(dept.getStatus());
                    deptUsers.add(sysDeptUser);
                });
            }
        });
        deptUserService.saveBatch(deptUsers);
        return password;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateUser(SysUserUpdateAo ao) {
        List<SysUserAddAo.DeptRoles> depts = ao.getDepts();
        List<SysUserAddAo.DeptRoles> validDepts =
                depts.stream().filter(t -> t.getStatus() == 0).collect(Collectors.toList());
        if (validDepts.size() == 0) {
            throw new BizException(USER_HAS_NOT_DEPTS);
        }
        SysUser oldUser = getById(ao.getUserId());
        if (oldUser == null) {
            throw new BizException(USER_NOT_EXISTS_ERROR);
        }
        SysUser user = BeanUtil.copyObject(ao, SysUser.class);
        updateById(user);
        //1先删除老的绑定
        LambdaQueryWrapper<SysDeptUser> queryWrapper = Wrappers.<SysDeptUser>lambdaQuery()
                .eq(SysDeptUser::getUserId, user.getUserId());
        deptUserService.remove(queryWrapper);
        //2再新增新的绑定
        List<SysDeptUser> deptUsers = Lists.newArrayList();
        depts.forEach(dept -> {
            if (CollectionUtil.isEmpty(dept.getRoleIds())) {
                SysDeptUser sysDeptUser = new SysDeptUser();
                sysDeptUser.setUserId(user.getUserId());
                sysDeptUser.setDeptId(dept.getDeptId());
                sysDeptUser.setStatus(dept.getStatus());
                deptUsers.add(sysDeptUser);
            } else {
                dept.getRoleIds().forEach(role -> {
                    SysDeptUser sysDeptUser = new SysDeptUser();
                    sysDeptUser.setUserId(user.getUserId());
                    sysDeptUser.setDeptId(dept.getDeptId());
                    sysDeptUser.setRoleId(role);
                    sysDeptUser.setStatus(dept.getStatus());
                    deptUsers.add(sysDeptUser);
                });
            }
        });
        deptUserService.saveBatch(deptUsers);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void delete(Long userId) {
        SysUser oldUser = getById(userId);
        if (oldUser == null) {
            throw new BizException(USER_NOT_EXISTS_ERROR);
        }
        removeById(userId);
        //1先删除老的绑定
        LambdaQueryWrapper<SysDeptUser> queryWrapper = Wrappers.<SysDeptUser>lambdaQuery()
                .eq(SysDeptUser::getUserId, userId);
        deptUserService.remove(queryWrapper);
    }

    @Override
    public List<DeptRoleVo> queryDeptRoles(Long userId) {
        List<DeptRoleInner> deptRoleVos = baseMapper.queryDeptRoles(userId);
        Map<Long, List<DeptRoleInner>> collect =
                deptRoleVos.stream().collect(Collectors.groupingBy(DeptRoleInner::getDeptId));
        List<DeptRoleVo> result = Lists.newArrayList();
        collect.forEach((k, v) -> {
            DeptRoleInner deptRoleInner = v.get(0);
            DeptRoleVo roleVo = new DeptRoleVo();
            roleVo.setDeptId(k);
            roleVo.setDeptName(deptRoleInner.getDeptName());
            roleVo.setStatus(deptRoleInner.getStatus());
            roleVo.setRoleIds(Lists.newArrayList());
            roleVo.setRoleNames(Lists.newArrayList());
            v.forEach(inner -> {
                roleVo.getRoleIds().add(inner.getRoleId());
                roleVo.getRoleNames().add(inner.getRoleName());
            });
            result.add(roleVo);
        });
        return result;

    }

    @Override
    public void enableUser(SysUserEnableAo ao) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(ao.getUserId());
        sysUser.setStatus(ao.getStatus());
        updateById(sysUser);
    }

    @Override
    public void changePwd(SysUserChangePwdAo ao) {
        MyUserDetails myUserDetails = UserContext.currentUser();
        SysUser sysUser = getById(myUserDetails.getUserId());
        boolean matches = passwordEncoder.matches(ao.getOriginalPwd(), sysUser.getPassword());
        if (!matches) {
            throw new BizException(USER_PASSWORD_INCORRECT_ERROR);
        }
        if (ao.getNewPwd().equals(ao.getOriginalPwd())) {
            throw new BizException(USER_PASSWORD_NEW_AND_OLD_SAME_ERROR);
        }
        if (!ao.getNewPwd().equals(ao.getRepeatPwd())) {
            throw new BizException(USER_PASSWORD_NOT_SAME_ERROR);
        }
        SysUser updateUser = new SysUser();
        updateUser.setUserId(sysUser.getUserId());
        updateUser.setPassword(passwordEncoder.encode(ao.getNewPwd()));
        updateById(updateUser);
    }

    @Override
    public void modifyBasicInfo(SysUserUpdateAo ao) {
        SysUser user = BeanUtil.copyObject(ao, SysUser.class);
        updateById(user);

        SysUser byId = getById(ao.getUserId());
        MyUserDetails myUserDetails = UserContext.currentUser();
        myUserDetails.reload(byId);
    }

}
